package com.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;

/**
 * @author 惊云
 * @date 2024/1/13 8:30
 */
@Component
public class TraceFilter extends OncePerRequestFilter {

    @Value("${spring.application.name}")
    private String appName;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException, ServletException, IOException {
        // 修改cookie
        ModifyHttpServletRequestWrapper mParametersWrapper = new ModifyHttpServletRequestWrapper(request);

        mParametersWrapper.addParameter("", "");
        mParametersWrapper.putCookie("", "");



        String requestURI = request.getRequestURI();
        String localHost = InetAddress.getLocalHost().getHostAddress();

        String traceInfo = request.getHeader("traceInfo");
        if(traceInfo == null){
            traceInfo = "";
        }
        traceInfo = traceInfo +appName +":"+ requestURI + ":" +localHost +";";

        mParametersWrapper.addHeader("traceInfo", traceInfo);

        // finish
        filterChain.doFilter(mParametersWrapper, response);
    }

    private class ModifyHttpServletRequestWrapper extends HttpServletRequestWrapper {

        private Map<String, String> mapCookies;
        //将request对象中的参数修改后，放在这个集合里，随后项目取的所有Parameter都是从这个集合中取数
        private Map<String, String[]> params;
        private Map<String, String> headerMap;

        ModifyHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);

            this.mapCookies = new HashMap<>();
            this.params = new HashMap<>();
            this.headerMap = new HashMap<>();
        }

        public void addHeader(String name, String value) {
            headerMap.put(name, value);
        }

        @Override
        public String getHeader(String name) {
            System.out.println("getHeader >>> " + name);
            String headerValue = super.getHeader(name);
            if (headerMap.containsKey(name)) {
                headerValue = headerMap.get(name);
            }
            return headerValue;
        }

        /**
         * get the Header names
         */
        @Override
        public Enumeration<String> getHeaderNames() {
            List<String> names = Collections.list(super.getHeaderNames());
            for (String name : headerMap.keySet()) {
                names.add(name);
            }
            return Collections.enumeration(names);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
//            System.out.println("getHeaders >>> " + name);
            List<String> values = Collections.list(super.getHeaders(name));
            if (headerMap.containsKey(name)) {
                values = Arrays.asList(headerMap.get(name));
            }
            return Collections.enumeration(values);
        }

        @Override
        public String getParameter(String name) {
//            System.out.println("getParameter >>> " + name);
            String[] strings = params.get(name);
            return strings != null ? strings[0] : null;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
//            System.out.println("getParameterMap >>> ");
            Map<String, String[]> parameterMap = super.getParameterMap();
            return parameterMap;
        }

        @Override
        public String[] getParameterValues(String name) {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            if (requestAttributes != null) {
                HttpServletRequest requestTmp = ((ServletRequestAttributes) requestAttributes).getRequest();
//                CookieUtils.getCookieValue(requestTmp, "");
            }
            String[] parameterValues = super.getParameterValues(name);
            System.out.println("getParameterValues >>> " + name);
            if(params.containsKey(name)){
                return params.get(name);
            }
            return parameterValues;
        }

        public void addParameter(String name, Object value) {//增加参数
            if (value != null) {
                if (value instanceof String[]) {
                    params.put(name, (String[]) value);
                } else if (value instanceof String) {
                    params.put(name, new String[]{(String) value});
                } else {
                    params.put(name, new String[]{String.valueOf(value)});
                }
            }
        }

        void putCookie(String name, String value) {
            this.mapCookies.put(name, value);
        }

        @Override
        public Cookie[] getCookies() {
            HttpServletRequest request = (HttpServletRequest) getRequest();
            Cookie[] cookies = request.getCookies();
            if (mapCookies == null || mapCookies.isEmpty()) {
                return cookies;
            }
            if (cookies == null || cookies.length == 0) {
                List<Cookie> cookieList = new LinkedList<>();
                for (Map.Entry<String, String> entry : mapCookies.entrySet()) {
                    String key = entry.getKey();
                    if (key != null && !"".equals(key)) {
                        cookieList.add(new Cookie(key, entry.getValue()));
                    }
                }
                if (cookieList.isEmpty()) {
                    return cookies;
                }
                return cookieList.toArray(new Cookie[cookieList.size()]);
            } else {
                List<Cookie> cookieList = new ArrayList<>(Arrays.asList(cookies));
                for (Map.Entry<String, String> entry : mapCookies.entrySet()) {
                    String key = entry.getKey();
                    if (key != null && !"".equals(key)) {
                        for (int i = 0; i < cookieList.size(); i++) {
                            if (cookieList.get(i).getName().equals(key)) {
                                cookieList.remove(i);
                            }
                        }
                        cookieList.add(new Cookie(key, entry.getValue()));
                    }
                }
                return cookieList.toArray(new Cookie[cookieList.size()]);
            }
        }

    }

}
